//
// Define class HttpServer.
//

#include "demo/http_server.h"

#include <algorithm>
#include <boost/any.hpp>
#include <boost/bind.hpp>

#include "core/event_loop_thread_group.h"
#include "core/inet_address.h"
#include "core/tcp_connection.h"
#include "demo/http_context.h"
#include "demo/http_request.h"
#include "demo/http_response.h"

using namespace core;

namespace demo {

const char HttpServer::kCRLF[] = "\r\n";

HttpServer::HttpServer(const core::InetAddress &address,
    const std::string &name) : server_(address, name) {}

HttpServer::~HttpServer() {}

bool HttpServer::Initialize() {
  this->thread_pool_ = new (std::nothrow) EventLoopThreadGroup();
  if(this->thread_pool_ == NULL) {
    return false;
  }
  if(this->thread_pool_->Initialize(5, "httpthread") == false) {
    CoreLog(ERROR, "%s:%d (%s) Initialize thread pool error.", __FILE__, __LINE__, __FUNCTION__);
    return false;
  }
  if(this->server_.Initialize(this->thread_pool_->GetNextLoop(), this->thread_pool_) == false) {
    CoreLog(ERROR, "%s:%d (%s) Initialize tcp server error.", __FILE__, __LINE__, __FUNCTION__);
    return false;
  }
  this->server_.SetConnectionCallback(boost::bind(&HttpServer::OnConnection, this, _1));
  this->server_.SetMessageCallback(boost::bind(&HttpServer::OnMessage, this, _1, _2, _3));
  this->server_.SetWriteCallback(boost::bind(&HttpServer::OnWriteComplete, this, _1));
  return true;
}

void HttpServer::Start() {
  this->thread_pool_->Start();
  this->server_.Start();
}

void HttpServer::OnConnection(core::TCPConnection *connection) {
  if(connection->CheckConnected()) {
    connection->GetContext() = HttpContext();
    // std::cout << "OnConnection ok" << std::endl;
  } else {
    HttpContext *context = &boost::any_cast<HttpContext &>(connection->GetContext());
    assert(context);
    context->Reset();
  }
}

void HttpServer::OnMessage(TCPConnection *connection,
    ByteBufferDynamic &buffer, const TimeTick &time_tick) {
  // std::cout << "OnMessage" << std::endl;
  HttpContext *context = &boost::any_cast<HttpContext &>(connection->GetContext());
  assert(context);
  core::TimestampMillisecond timestamp(time_tick);
  if(this->ParseRequest(&buffer, context, timestamp) == false) {
    connection->SendMessage("HTTP/1.1 400 Bad Request\r\n\r\n");
    connection->Shutdown();
  } else {
    if(context->CheckExpectAll()) {
      HttpRequest &request = context->GetRequest();
      std::string check_connection(request.GetHeader("Connection"));
      bool close = check_connection == "close" ||
        (request.GetVersion() == HttpRequest::HTTP10 && check_connection != "Keep-Alive");
      HttpResponse response(close);
      this->http_callback_(request, &response);
      core::ByteBufferDynamic buf(8);
      response.ToBuffer(&buf);
      connection->SendMessage(buf.ReadPointer(), buf.ReadSize());
      if(response.CheckCloseConnection()) {
        request.SetCloseConnection();
        connection->Shutdown();
      }
    }
  }
}

void HttpServer::OnWriteComplete(core::TCPConnection *connection) {
  CoreLog(INFO, "%s:%d (%s) Send complete.", __FILE__, __LINE__, __FUNCTION__);
  assert(connection);
  HttpContext *context = &boost::any_cast<HttpContext &>(connection->GetContext());
  assert(context);
  context->Reset();
  if(context->GetRequest().CheckCloseConnection()) {
    connection->Shutdown();
  }
}

bool HttpServer::ProcessRequestLine(const char* begin, const char* end,
    HttpContext* context) {
  bool succeed = false;
  const char* start = begin;
  const char* space = std::find(start, end, ' ');
  HttpRequest& request = context->GetRequest();
  if(space != end && request.SetMethod(start, space)) {
    start = space+1;
    space = std::find(start, end, ' ');
    if(space != end) {
      request.SetPath(start, space);
      start = space+1;
      succeed = end-start == 8 && std::equal(start, end-1, "HTTP/1.");
      if(succeed) {
        if(*(end-1) == '1') {
          request.SetVersion(HttpRequest::HTTP11);
        } else if (*(end-1) == '0') {
          request.SetVersion(HttpRequest::HTTP11);
        } else {
          succeed = false;
        }
      }
    }
  }
  return succeed;
}

bool HttpServer::ParseRequest(ByteBufferDynamic *buf, HttpContext* context,
    const core::TimestampMillisecond& timestamp) {
  bool ok = true;
  bool hasMore = true;
  while(hasMore) {
    if(context->CheckExpectRequestLine()) {
      if(buf->ReadSize() >= 2) {
        const char* crlf = std::search(buf->ReadPointer(),
            static_cast<const char*>(buf->WritePointer()), kCRLF, kCRLF + 2);
        if(crlf) {
          ok = ProcessRequestLine(buf->ReadPointer(), crlf, context);
          if(ok) {
            context->GetRequest().SetReceiveTime(timestamp);
            assert(buf->ReadPointer() <= crlf + 2);
            buf->ReadShift(crlf + 2 - buf->ReadPointer());
            context->ReceiveRequestLine();
          } else {
            hasMore = false;
          }
        } else {
          hasMore = false;
        }
      } else {
        hasMore = false;
      }
    } else if(context->CheckExpectHeader()) {
      if(buf->ReadSize() >= 2) {
        const char* crlf = std::search(buf->ReadPointer(),
            static_cast<const char*>(buf->WritePointer()), kCRLF, kCRLF + 2);
        if(crlf) {
          const char* colon = std::find(buf->ReadPointer(), crlf, ':');
          if(colon != crlf) {
            context->GetRequest().AddHeader(buf->ReadPointer(), colon, crlf);
          } else {
            context->ReceiveHeader();
            hasMore = !context->CheckExpectAll();
          }
          assert(buf->ReadPointer() <= crlf + 2);
          buf->ReadShift(crlf + 2 - buf->ReadPointer());
        } else {
          hasMore = false;
        }
      } else {
        hasMore = false;
      }
    } else if (context->CheckExpectBody()) {
    }
  }

  return ok;
}


}  // namespace demo

